package 算法.leetcode.offer;

import java.util.*;

/**
 * https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/
 *
 * @author lchenglong
 * @date 2022/4/25
 */
public class Offer20 {

    public static void main(String[] args) {
        Offer20 instance = new Offer20();
        List<String> list  = new ArrayList(Arrays.asList("-1E-16","..","12e", "1a3.14", "1.2.3", "+-5", "12e+5.4","3.",".","e9","0e","-."));
        for(String s: list) {
            boolean ans = instance.isNumber3(s);
            System.out.println(s+": " +ans);
        }
    }
    public boolean isNumber(String s) {
        // 去掉前后空格
        s = s.trim();
        if (s.length() == 0)return false;
        // 判断第一位是否符合条件
        char c = s.charAt(0);
        int sfIdx = -1;
        boolean sf = false;
        boolean xs = false;
        if (c != '+' && c !='-' && c != '.' && (c<'0'|| c>'9'))return false;
        if ((c<'0')&&s.length()==1)return false;
        else if (c == '.') xs = true;

        for (int i=1;i<s.length();i++) {
            // 对于科学计算法的判断
            if (s.charAt(i) == 'e'|| s.charAt(i) == 'E') {
                // 重复出现E
                if (sf)return false;
                if (i == s.length()-1)return false;
                else if (i == 1){
                    if (c == '+' || c =='-'||c=='.')return false;
                }
                sf = true;
                sfIdx= i;
            } else if (s.charAt(i) == '.'){
                // 对于小数的判断
                // 重复出现.
                if (xs)return false;
                if (sf)return false;
                xs = true;
                if (i == s.length()-1 && (s.charAt(i-1)>'9'||s.charAt(i-1)<'0'))return false;
            } else if (s.charAt(i)<'0'||s.charAt(i)>'9'){
                if (sf&&(s.charAt(i)=='+'||s.charAt(i)=='-')&&(sfIdx==i-1)) {
                    if (i == s.length()-1)return false;
                } else return false;
            }

        }
        return true;
    }

    public boolean isNumber2(String s) {
        // 去掉前后空格
        s = s.trim();
        if (s.length() == 0)return false;
        // 转小写
        s = s.toLowerCase();
        // 区分科学计数法
        String[] es = s.split("e");
        if (es.length>2)return false;
        else if (es.length==1){
            return sp(es[0]);
        }
        else if (es.length==2){
            if (es[0].length() == 0||es[1].length()==0)return false;
            return sp(es[0])&&sn(es[1]);
        }
        else return false;
    }

    private boolean sp(String s){
        char c = s.charAt(0);
        boolean sf = false;
        boolean xs = false;
        if (c != '+' && c !='-' && c != '.' && (c<'0'|| c>'9'))return false;
        if ((c<'0')&&s.length()==1)return false;
        else if (c == '.') xs = true;

        for (int i=1;i<s.length();i++) {
            // 对于科学计算法的判断
            if (s.charAt(i) == '.'){
                // 对于小数的判断
                // 重复出现.
                if (xs)return false;
                if (sf)return false;
                xs = true;
            } else if (s.charAt(i)<'0'||s.charAt(i)>'9')return false;

        }
        return true;
    }

    private boolean sn(String s){
        char c = s.charAt(0);
        if (c != '+' && c !='-' && (c<'0'|| c>'9'))return false;
        if ((c<'0')&&s.length()==1)return false;

        for (int i=1;i<s.length();i++) {
           if (s.charAt(i)<'0'||s.charAt(i)>'9')return false;
        }
        return true;
    }

    public boolean isNumber3(String s) {
        Map[] states = {
                //0：规定0是初值，字符串表示数值，有4种起始状态，开头空格、符号、数字、前面没有数的小数点
                //其中 开头空格 还是指向states[0]，上一位是 开头空格，下一位可以是 空格、符号、数字、前面没有数的小数点
                new HashMap() {{ put(' ', 0); put('s', 1); put('d', 2); put('.', 4); }},
                //1：上一位是符号，符号位后面可以是 数字、前面没有数的小数点
                new HashMap() {{ put('d', 2); put('.', 4); }},
                //2：上一位是数字，数字的下一位可以是 数字、前面有数的小数点、e、结尾空格
                new HashMap() {{ put('d', 2); put('.', 3); put('e', 5); put(' ', 8); }},
                //3：上一位是前面有数的小数点，下一位可以是 数字、e（8.e2 = 8e2，和2的情况一样）、结尾空格
                new HashMap() {{ put('d', 3); put('e', 5); put(' ', 8); }},
                //4：上一位是前面没有数的小数点，下一位只能是 数字（符号肯定不行，e得前面有数才行）
                new HashMap() {{ put('d', 3); }},
                //5：上一位是e，下一位可以是 符号、数字
                new HashMap() {{ put('s', 6); put('d', 7); }},
                //6：：上一位是e后面的符号，下一位只能是 数字
                new HashMap() {{ put('d', 7); }},
                //7：上一位是e后面的数字，下一位可以是 数字、结尾空格
                new HashMap() {{ put('d', 7); put(' ', 8); }},
                //8：上一位是结尾空格，下一位只能是 结尾空格
                new HashMap() {{ put(' ', 8); }}
        };
        int p = 0;
        char t;
        //遍历字符串，每个字符匹配对应属性并用t标记，非法字符标记？
        for(char c : s.toCharArray()) {
            if(c >= '0' && c <= '9') t = 'd';
            else if(c == '+' || c == '-') t = 's';
            else if(c == 'e' || c == 'E') t = 'e';
            else if(c == '.' || c == ' ') t = c;
            else t = '?';
            //当前字符标记和任何一种当前规定格式都不匹配，直接返回false
            if(!states[p].containsKey(t)) return false;
            //更新当前字符的规定格式，进入下一个规定的Map数组
            p = (int)states[p].get(t);
        }
        //2（正、负整数）、3（正、负小数）、7（科学计数法）、8（前三种形式的结尾加上空格）
        //只有这四种才是正确的结尾
        return p == 2 || p == 3 || p == 7 || p == 8;
    }

}
